package com.keyholesoftware.trango.dao.jpa;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Transactional;

import com.keyholesoftware.trango.dao.Dao;
import com.keyholesoftware.trango.dao.DaoException;

public class JPADao<T, ID extends Serializable> implements Dao<T, ID> {

	private final EntityManager entityManager;
	private Class<T> type;

	public JPADao(EntityManager entityManager, Class<T> type) {
		super();
		this.entityManager = entityManager;
		this.type = type;
	}

	@Transactional(readOnly = true)
	public Long count() {
		return (Long) entityManager.createQuery("select count(o) from " + type.getName() + " o").getSingleResult();
	}

	public Query createQuery(String query) {
		return entityManager.createQuery(query);
	}

	@Transactional(readOnly = true)
	public T find(ID id, String... eagerPaths) {
		return entityManager.find(type, id);
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<T> find(String query) throws DaoException {
		String queryString = "from " + type.getName() + " " + query;
		Query queryObject = entityManager.createQuery(queryString);
		return queryObject.getResultList();
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<T> findAll(String... eagerPaths) {
		String queryString = "from " + type.getName();
		Query queryObject = entityManager.createQuery(queryString);
		return queryObject.getResultList();
	}

	@SuppressWarnings("unchecked")
	public List<T> findEntries(int firstResult, int maxResults) {
		return entityManager.createQuery("select o from " + type.getName() + " o").setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
	}

	@Transactional
	public T merge(T entity) {
		T merged = entityManager.merge(entity);
		entityManager.flush();
		return merged;
	}

	@Transactional
	public T persist(T entity) {
		entityManager.persist(entity);
		return entity;
	}

	@Transactional
	public void remove(ID id, T entity) {
		if (!entityManager.contains(entity)) {
			entity = entityManager.find(type, id);
		}
		entityManager.remove(entity);
	}

	public void setType(Class<T> type) {
		this.type = type;
	}
}
